package abc.config;

import abc.database.Interceptor.ISqlInterceptorFactory;
import abc.model.DSInfo;
import abc.utils.StringUtil;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Configuration
public class MybatisConfig {

    @Value("${spring.datasource.dynamic.primary}")
    protected String primaryDatasource;

    @Value("${mybatis-plus.mapper-locations:classpath:/abc/**/**/mapper/xml/*Mapper.xml}")
    protected String mapperLocations;

    @Value("${mybatis-plus.configuration.log-impl:}")
    protected String logImpl;

    @Resource
    protected DatasourceConfigure datasourceConfigure;

    @Autowired(required = false)
    protected MetaObjectHandler metaObjectHandler;

    @Autowired
    DataSource dataSource;

    @Autowired(required = false)
    ISqlInjector iSqlInjector;

    @Autowired(required = false)
    ISqlInterceptorFactory sqlInterceptorFactory;


    /** 全局自定义配置 */
    @Bean(name = "globalConfig")
    @ConfigurationProperties(prefix = "mybatis-plus.global-config")
    public GlobalConfig globalConfig() {
        GlobalConfig conf = new GlobalConfig();
        conf.setDbConfig(new GlobalConfig.DbConfig().setColumnFormat("`%s`"));
        if(iSqlInjector == null) {
            iSqlInjector = new DefaultSqlInjector() {
                /**
                 * 注入自定义全局方法
                 */
                @Override
                public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
                    List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
                    // 不要逻辑删除字段, 不要乐观锁字段, 不要填充策略是 UPDATE 的字段
                    methodList.add(new InsertBatchSomeColumn(t -> !t.isLogicDelete() && !t.isVersion() && t.getFieldFill() != FieldFill.UPDATE));
                    // 不要填充策略是 INSERT 的字段, 不要字段名是 column4 的字段
                    methodList.add(new AlwaysUpdateSomeColumnById(t -> t.getFieldFill() != FieldFill.INSERT ));
                    return methodList;
                }
            };
        }

        conf.setSqlInjector(iSqlInjector);
        return conf;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(GlobalConfig globalConfig) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setGlobalConfig(globalConfig());
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        //  添加分页功能
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        if(sqlInterceptorFactory !=null && sqlInterceptorFactory.getInterceptors()!=null) {
            for(InnerInterceptor interceptor : sqlInterceptorFactory.getInterceptors()) {
                if(interceptor == null) {
                    continue;
                }
                mybatisPlusInterceptor.addInnerInterceptor(interceptor);
            }
        }
        sessionFactory.setPlugins(mybatisPlusInterceptor);

        if(StringUtil.isNotEmpty(logImpl)) {
            //日志打印
            TransactionFactory transactionFactory = new JdbcTransactionFactory();
            Environment environment = new Environment("Production", transactionFactory, dataSource);
            MybatisConfiguration configuration = new MybatisConfiguration(environment);
            if("org.apache.ibatis.logging.stdout.StdOutImpl".equals(logImpl)) {
                configuration.setLogImpl(StdOutImpl.class);
            }
            else {
                configuration.setLogImpl((Class<? extends Log>) Class.forName(logImpl));
            }
            configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
            sessionFactory.setConfiguration(configuration);
        }
        /* 自动填充插件 */
        if(metaObjectHandler != null) {
            globalConfig.setMetaObjectHandler(metaObjectHandler);
        }
        sessionFactory.setGlobalConfig(globalConfig);
        return sessionFactory.getObject();
    }

    @Bean
    @ConditionalOnProperty(prefix = "seata", name = "enableAutoDataSourceProxy", havingValue = "false", matchIfMissing = true)
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        Optional<Map.Entry<String, DSInfo>> configOptional = datasourceConfigure.getDatasource().entrySet().stream().filter(a -> a.getKey().equals(primaryDatasource)).findFirst();
        Map.Entry<String, DSInfo> config = null;
        if(configOptional.isPresent()) {
            config = configOptional.get();
        }
        else if(datasourceConfigure.getDatasource().entrySet().size()>0) {
            config = datasourceConfigure.getDatasource().entrySet().iterator().next();
        }
        if(config != null) {
            dataSource.setDriverClassName(config.getValue().getDriverClassName());
            dataSource.setUrl(config.getValue().getUrl());
            dataSource.setUsername(config.getValue().getUsername());
            dataSource.setPassword(config.getValue().getPassword());
        }
        return dataSource;
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
